#pragma once

#include<iostream>
#include<cstring>
#include<unistd.h>
#include<string>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include"nocopy.hpp"
#include"Log.hpp"
#include"InetAddr.hpp"

using namespace log_ns;

static const int gsockfd=-1;
static const uint16_t glocalport=8888;

enum
{
    SOCKET_ERROR=1,
    BAND_ERROR
};

using service_t=std::function<void(int,const std::string message,InetAddr &who)>;

class UdpServer:public nocopy
{
public:
    UdpServer(service_t func,uint16_t localport = glocalport)
        :_func(func)
        ,_sockfd(gsockfd)
        ,_localport(localport)
        ,_isrunning(false)
    {}

    void InitServer()
    {
        //1.创建套接字(文件)  
        _sockfd=::socket(AF_INET,SOCK_DGRAM,0); //调用系统级的方法
        if(_sockfd<0)
        {
            //通信不可能实现，直接退出
            LOG(FATAL,"socket error\n");
            exit(SOCKET_ERROR);
        }

        LOG(DEBUG,"socket creat success, _sockfd:%d\n",_sockfd); //_socked=3

        //2.bind
        //(1)先填充本地信息
        struct sockaddr_in local;
        memset(&local,0,sizeof(local));
        local.sin_family=AF_INET;
        local.sin_port=htons(_localport); //端口号需要从主机转换成网络序列
        /*
        local.sin_addr.s_addr=inet_addr(_localip.c_str());             //用户习惯的是字符串，比如"192.xxx.xxx.xxx"
        //但是网络中需要4字节ip，需要的是网络序列ip
        //也就是说这里需要将字符串转换成4字节和网络序列
        */
       local.sin_addr.s_addr=INADDR_ANY;

        //(2)绑定
        int n=::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
        if(n<0)
        {
            //绑定失败，不会网络通信
            LOG(FATAL,"bind error\n");
            exit(BAND_ERROR);
        }
        //绑定成功
        LOG(DEBUG,"socket bind success\n");
    }

    void Start()
    {
        _isrunning=true;
        char message[1024];
        while (_isrunning)
        {
            struct sockaddr_in peer;
            socklen_t len=sizeof(peer);
            
            ssize_t n=recvfrom(_sockfd,message,sizeof(message)-1,0,(struct sockaddr*)&peer,&len);
            if(n>0) 
            {
                InetAddr addr(peer);
                // uint16_t peerport=ntohs(peer.sin_port);
                // std::string peerip=inet_ntoa(peer.sin_addr);

                message[n]=0;
                LOG(DEBUG,"[%s]# %s\n",addr.AddrStr().c_str(),message);
                _func(_sockfd,message,addr);
                LOG(DEBUG,"return udpserver\n");
            }
            else
            {
                std::cout<<"recvfrom : error "<<std::endl;
            }
        }
        
    }

    ~UdpServer()
    {
        if(_sockfd>gsockfd)::close(_sockfd);
    }

private:
    int _sockfd;  //读写都使用同一个sockfd，反应说明UDP是全双工通信
    uint16_t _localport; //服务器的端口号
    bool _isrunning;

    service_t _func;
};